#include <std.h>
#include <system.h> 
#include <pio.h>
#include <pit.h>
#include <error.h>

#define PIT_INIT_COMMAND 0x36 

void halt_sys();

void setclock(unsigned long hz)
{
    unsigned short divisor = PIT_HZ / hz;
	outb(PIT_CONTROL, PIT_INIT_COMMAND);
	outportw(PIT_CNTR0, divisor);
}

#define INITFLAGS 0x212
volatile CPU_state proc_fooler;

void halt_cpu();

void process_return()
{
	char *cproc;
	cproc=(char *)running_process_name();
	printf("\nProcess return::%s",cproc);
	delete_process(cproc);
	while(1);
}

/*Critical protections for below two functions have been provided in the functions in sched.c
which use these functions*/

int load_process(CPU_state *state)
{
	proc_fooler.eax=state->eax; 
	proc_fooler.ebx=state->ebx;
	proc_fooler.ecx=state->ecx;
	proc_fooler.edx=state->edx;
	proc_fooler.esi=state->esi;
	proc_fooler.edi=state->edi;
	proc_fooler.fs=state->fs;
	proc_fooler.gs=state->gs;
	proc_fooler.es=state->es;
	proc_fooler.ds=state->ds;
	proc_fooler.esp=state->esp;
	proc_fooler.ebp=state->ebp;
	proc_fooler.flags=state->flags;
	return NULL;
}

int save_process(CPU_state *state)
{
	state->eax=proc_fooler.eax;
	state->ebx=proc_fooler.ebx;
	state->ecx=proc_fooler.ecx;
	state->edx=proc_fooler.edx;
	state->esi=proc_fooler.esi;
	state->edi=proc_fooler.edi;
	state->fs=proc_fooler.fs;
	state->gs=proc_fooler.gs;
	state->es=proc_fooler.es;
	state->ds=proc_fooler.ds;
	state->esp=proc_fooler.esp;
	state->ebp=proc_fooler.ebp; 
	state->flags=proc_fooler.flags;
	return NULL;
}

#define SYS_DATA_SEL 0x10
	
int init_cpu_state(CPU_state *state)
{
	state->eax=NULL;
	state->ebx=NULL;
	state->ecx=NULL;
	state->edx=NULL;
	state->esi=NULL;
	state->edi=NULL;
	state->fs=SYS_DATA_SEL;
	state->gs=SYS_DATA_SEL;
	state->es=SYS_DATA_SEL;
	state->ds=SYS_DATA_SEL; 
	state->esp=NULL;
	state->ebp=NULL;
	state->flags=INITFLAGS;
}

void show_state(CPU_state *state)
{
	printf("\nState:\neax:%x\tebx:%x\necx:%x\tedx:%x\nesi:%x\tedi:%x\nesp:%x\tebp:%x\neflags:%x\nfs:%x\tgs:%x\nes:%x\tds:%x", \
		state->eax,state->ebx,state->ecx,state,state->esi,state->edi,state->esp,state->ebp,state->flags, \
		state->fs,state->gs,state->es,state->ds);
}


extern unsigned long _set_stack(unsigned long arch_stack,void *entry,unsigned long init_flags);
int set_stack(CPU_state *state,void *entry,unsigned long stack)
{
	state->esp=_set_stack(stack,entry,INITFLAGS);
	state->ebp=state->esp;
}	
	
unsigned char inportb(unsigned short _port)
{
  unsigned char result;
  __asm__  ("in %%dx, %%al" : "=a" (result) : "d" (_port));
  return result;
}

void outportb(unsigned short _port, unsigned char _data)
{
  __asm__ ("out %%al, %%dx" : :"a" (_data), "d" (_port));
}

void outportw(unsigned short _port,unsigned short _data)
{
	outb(_port,(unsigned char)_data);
	outb(_port,(unsigned char)(_data>>8));
}

/*It is not allowed to use these functions cli and sti directly... start_mono and release_mono can be used*/

inline cli(void)
{
	__asm__ ("cli");
}

inline sti(void)
{
	__asm__ ("sti");
}	

void __cpu(void)
{
	getcpu();
	printf("::\neax:%x ebx:%x ecx:%x edx:%x esp:%x ebp:%x esi:%x edi:%x",cpu_eax,cpu_ebx,cpu_ecx,cpu_edx,cpu_esp,cpu_ebp,cpu_esi,cpu_edi);
	printf("\ncs:%x ds:%x ss:%x fs:%x gs:%x es:%x\neflags:%x",cpu_cs,cpu_ds,cpu_ss,cpu_fs,cpu_gs,cpu_es,cpu_eflags);
	printf("\nCR0:%x CR3:%x",cpu_cr0,cpu_cr3);
	printf("\nEvent pushed phys address:0x%x",cpu_espp);
}


void default_interrupt()
{
	printf("\nUnknown interrupt");
}

void exception_process(char *string)
{
	cli();
	printf(string);
	printf(" generated by::%s",running_process_name());
	show_state((CPU_state *)currentprocess());
	delete_process(get_process_id(running_process_name()));
	printf("\nProcess terminated.");
	sti();
	while(1);
}

void exception(char *string)
{
	if(scheduler_up())	{
		exception_process(string);
	}
	__cpu();
	i_error("Init exception:%s",string);
}

void ex0(void)
{
	exception("\nDivision by zero");
}

void ex1(void)
{
	exception("\nDebug exc");
}

void ex2(void)
{
	exception("\nNMI (Non maskable Interrupt)");
}

void ex3(void)
{
	exception("\nBreakpoint");
}

void ex4(void)
{
	exception("\nINTO overflow");
}

void ex5(void)
{
	exception("\nOut of Bounds exc");
}

void ex6(void)
{
	exception("\nInvalid opcode in control path");
}

void ex7(void)
{
	exception("\nNo coprocessor");
}

void ex8(void)
{
	exception("\nDouble fault");
}

void ex9(void)
{
	exception("\nCoprocessor segment overrun");
}

void ex10(void)
{
	exception("\nBad TSS");
}

void ex11(void)
{
	exception("\nSegment not present");
}

void ex12(void)
{
	exception("\nStack fault");
}

void ex13(void)
{
	exception("\nGeneral protection fault");
}

void ex14(void)
{
	exception("\nPage fault");
}

void ex15(void)
{
	exception("\nUnknown interrupt (on exc)");
}

void ex16(void)
{
	exception("\nCoprocessor fault");
}

void ex17(void)
{
	exception("\nAlignment check");
}

void ex18(void)
{
	exception("\nMachine check");
}


void suspend_all()
{
	cli();
	printf("\nSystem halted!");
	halt_sys();
}

void set_page_dir(reg pgdiraddr)
{
	write_cr3(pgdiraddr);
	reload_pg();
}

void enable_pg()
{
	write_cr0(read_cr0() | 0x80000000);
	reload_pg();
}

void disable_pg()
{
	write_cr0(read_cr0() & 0x7FFFFFFF);
	write_cr3(0);
}

void init_biosi(void)
{
	printf("\ni386:Bios calls");
	load_lmembm();
}


extern void reload_tables();
void restore_pm()
{
	reload_tables();
	restore_irq_pm();
	setclock(SYSTEM_SCHEDULE_CLOCK_SPEED);
	restore_pg();
	release_mono();
}


void prepare_rm()
{
	start_mono();
	prepare_irq_rm();
	setclock(18);
	disable_pg();
}

void halt_cpu()
{
	cli();
	if(apm_installed())
		apm_poweroff();
	halt_sys();
}

void halt_sys()
{
	cli();
	asm("hlt");
	while(1);
}

void idle()
{
	printdev("\n<idle>");
	while(1)
		asm("hlt");
}

void halt(char type)
{
	end_all_processes();
	halt_cpu();
}

extern void callbios();
int bios(short sax,short sbx,short scx,short sdx,short ssi,short sdi,short intno)
{
	unsigned short *regs=(unsigned short *)0x500;
	unsigned long *bregs=(unsigned long *)0x514;
	*(regs+0x0)=sax;
	*(regs+0x2)=sbx;
	*(regs+0x4)=scx;
	*(regs+0x6)=sdx;
	*(regs+0x8)=ssi;
	*(regs+0x10)=sdi;
	*(regs+0x12)=intno;
	prepare_rm();
	callbios();
	restore_pm();
	ax=*(regs+0x0);
	bx=*(regs+0x2);
	cx=*(regs+0x4);
	dx=*(regs+0x6);
	si=*(regs+0x8);
	di=*(regs+0x10);
	f=*(regs+0x12);
	eax=*(bregs+0x0);
	ebx=*(bregs+0x4);
	ecx=*(bregs+0x8);
	edx=*(bregs+0xb);
	esi=*(bregs+0x10);
	edi=*(bregs+0x14);
}
